#ifndef GLMESH_H_
#define GLMESH_H_

//#define DRAW_BBOX

#include <vector>
#include <string>
#include <map>

#include "GLRenderer.h"
#include "Camera.h"
//#include "MyWindow.h"


#pragma push_macro("normalize")
#pragma push_macro("dot")
#pragma push_macro("cross")
#undef normalize
#undef dot
#undef cross
#define GLM_FORCE_RADIANS
#include <glm/mat4x4.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#pragma pop_macro("normalize")
#pragma pop_macro("dot")
#pragma pop_macro("cross")

class BaseObject{
protected:
	glm::vec3 bbox_min_;
	glm::vec3 bbox_max_;
	glm::vec3 center_;
public:
	glm::vec3 bbox_min(){return bbox_min_;}
	glm::vec3 bbox_max(){return bbox_max_;}
	glm::vec3 center(){return center_;}

	virtual bool TraceL(const float orig[3], const float dir[3], float* dist);

	virtual void Draw();
};

class MyBaseElement;

class MovableObject : public BaseObject{
protected:
	glm::vec3 orig_bbox_min_;
	glm::vec3 orig_bbox_max_;
	glm::mat4x4 model_matrix_;

	virtual void UpdateBBox();
public:
	virtual ~MovableObject(){};

	glm::mat4x4 model_matrix(){return model_matrix_;}

	virtual bool Check(float x, float y, glm::mat4x4& view_proj_mat, const float* dims, const int* dims_px, float* dist = nullptr);

	virtual void SetPos(float, float, float);
	virtual void SetMatrix(glm::mat4x4 mat);
	virtual void Move(float, float, float);

	virtual void SetAngles(float, float, float);
	virtual void Rotate(float, float, float);
	virtual void Rotate(glm::vec3, float);
	virtual void RotateX(float);
	virtual void RotateY(float);
	virtual void RotateZ(float);

	virtual void Rescale( const float old_box_min[3], const float old_box_max[3], const float new_box_min[3], const float new_box_max[3] );

	virtual MovableObject* Clone(){return new MovableObject(*this);};
};

class GLTriStrip{
public:
	GLuint offset;
	GLuint num_indices;
	//GLuint tex_id;
	std::shared_ptr<GLTexture> tex;
};

class GLMesh : public BaseObject{
	std::string name_;
	std::string file_name_;

	std::vector<float> vtx_attribs_;
	std::vector<unsigned short> vtx_indices_;
	GLuint attribs_buf_id_;
	GLuint indices_buf_id_;

	std::vector<GLTriStrip> strips;

	std::vector<std::string> texture_names_;
	//std::vector<unsigned int> tex_ids_;

public:
	virtual ~GLMesh();

	std::string name(){return name_;}
	std::string file_name(){return file_name_;}

	void LoadFromFile(std::string file_name);
	void CleanUp();

	void Draw();

	bool TraceL(const float orig[3], const float dir[3], float* dist);
};

class GLStaticMesh : public MovableObject{
	std::string name_;
	std::shared_ptr<GLMesh> p_mesh_;

	//virtual void UpdateBBox();
public:
	std::string name(){return name_;}
	std::shared_ptr<GLMesh> p_mesh(){return p_mesh_;}

	glm::vec3 bbox_min(){return bbox_min_;}
	glm::vec3 bbox_max(){return bbox_max_;}

	void set_name(std::string name){name_ = name;};

	void LoadFromFile(std::string file_name);

	void Draw();

	//virtual bool Check(float x, float y, glm::mat4x4& view_proj_mat, MyBaseElement& element);
	virtual bool TraceL(const float orig[3], const float dir[3], float* dist);

	virtual GLStaticMesh* Clone(){return new GLStaticMesh(*this);};

	static int num_meshes;
};

static std::map<std::string, std::weak_ptr<GLMesh>> loaded_meshes;
std::shared_ptr<GLMesh> LoadMeshFromFile(std::string);
//void UnLoadMesh(std::string);

float DistPointToPoint( float p1[], float p2[], float* res_vec );
float DistPointToSegment( float v1[], float v2[], float p[], float* res_vec );

#endif /* GLMESH_H_ */
